package com.mastercard.labs

import java.text.SimpleDateFormat


class TransactionService {
	def cardService
	def mailService
	def smsService
	def grailsApplication
	/**
	 * Return transaction details for
	 * spending history
	 * @param fromDate
	 * @param toDate
	 * @return
	 */
	def postTransaction(transaction, user){
		log.info ("postTransaction")
		
		log.info "updateRewardPoint"
		def calculatePoint = grailsApplication.config.rewards.formular
		def reminderFormular = grailsApplication.config.budget_target.reminder.formular
		def rpList = RewardPoint.findAllByUser (user)
		if (!rpList || rpList.size() == 0){
			log.info "create new"
			def rp = new RewardPoint()
			rp.user = user
			log.info ("before " + transaction.amount)
			rp.point = calculatePoint(transaction.amount, null)
			log.info ("after "+ rp.point)
			rp.save()
		}else{
			log.info "update"
			def rp = rpList.get(0)
			rp.point += calculatePoint(transaction.amount, null)
			rp.save()
		}
		
		//update budget target
		def budgets = Budget.findAllByUserAndDeleted(user,false)
		if(budgets){
			budgets.each() {
				if(it.type == Budget.Type.ALL || it.card.number == transaction.card.number){
					def startDate = it.startDate
					def endDate = it.endDate
					def txnDate
					if(it.budgetType != Budget.BudgetType.CUSTOMIZE){
						def start
						def end
						Calendar cal = Calendar.getInstance();
						cal.setTime(transaction.txnDate)
						cal.clear(Calendar.HOUR_OF_DAY);
						cal.clear(Calendar.MINUTE);
						cal.clear(Calendar.SECOND);
						cal.clear(Calendar.MILLISECOND);
						txnDate = cal.getTime()
						if(it.budgetType == Budget.BudgetType.WEEKLY){
							cal.set(Calendar.DAY_OF_WEEK, cal.getFirstDayOfWeek());
							start = cal.getTime()
							cal.add(Calendar.DATE, 7)
						}else{
							cal.set(Calendar.DAY_OF_MONTH, it.billDate);
							start = cal.getTime()
							cal.add(Calendar.MONTH, 1)
						}
						end = cal.getTime()
						if(it.recursive){
							startDate = start
							endDate = end
						}else{
							if (startDate < start){
								startDate = start
							}
							if(endDate > end){
								endDate = end
							}
						}
					}
					log.info ("startDate = ${startDate}, endDate = ${endDate} ${it.id}")
					long budget_id = it.id
					if(txnDate >= startDate && txnDate < endDate){
						def beList = BudgetEntry.createCriteria().list {
							budget {
								eq("id", budget_id)
							}
							eq("active",true)
							le("startDate", txnDate)
							gt("endDate", txnDate)
						}
						
						def be
						if (!beList){
							log.info "create new BudgetEntry"
							be = new BudgetEntry()
							be.budget = it
							be.currentAmount = transaction.amount
							be.startDate = startDate
							be.endDate = endDate
							be.active = true
							be.save()
						}else{
							log.info "update budgetEntry number = ${beList.size()}"
							be = beList.get(0)
							be.currentAmount += transaction.amount
							be.save()
						}
						log.info "total current amount ${be.currentAmount}, ${it.amount}"
						if(be.currentAmount >= it.amount){
							log.info "trigger email or sms"
							def cardInfo = (it.type == Budget.Type.ALL)?"overall" : "card (${it.card.maskNumber})"
							def content = "Fr MasterCard MPEMS: your ${cardInfo} ${it.budgetType} spend (${be.currentAmount}) " + (it.budget?"exceed ${it.amount} budget":" meet ${it.amount} target") + (it.remarks? " with Remarks: ${it.remarks}" :"")
							if(it.smsNotification){
								log.info "send sms ${user.mobile}"
								smsService.send(user.mobile, content)
								//def data = new URL("http://137.132.247.154:8888/SMS/send.aspx?rep=${user.mobile}&msg=MasterCard-MPEMS").getText()
								//log.info "data =  ${data}"
							}
							if(it.emailNotification){
								log.info "send email"									
								try{
									mailService.sendMail {
										to user.email
										from "mpems.mastercard@gmail.com"
										subject "MasterCard MPEMS Notification"
										body "Dear user, \n${content}\n\nRegerds\nMasterCard-MPEMS"
									 }
								}
								catch (e){
									log.info("email failed to send")
								}
							}
						}else if (reminderFormular (be.currentAmount, it.amount)){
							log.info "trigger reminder email or sms"
							def cardInfo = (it.type == Budget.Type.ALL)?"overall" : "card (${it.card.maskNumber})"
							def content = "Fr MasterCard MPEMS: Gendle Reminder, your ${cardInfo} ${it.budgetType} spend (${be.currentAmount}) nearly " + (it.budget?"exceed ${it.amount} budget":" meet ${it.amount} target") + (it.remarks? " with Remarks: ${it.remarks}" :"")

							if(it.smsNotification){
								log.info "send sms ${user.mobile}"
								smsService.send(user.mobile, content)
								//def data = new URL("http://137.132.247.154:8888/SMS/send.aspx?rep=${user.mobile}&msg=MasterCard-MPEMS").getText()
								//log.info "data =  ${data}"
							}
							if(it.emailNotification){
								log.info "send email"
								def text = it.budget?" exceed budget ":" meet target "
								log.info "Dear user, \n your current spending ${be.currentAmount} nearly exceed monthly budget ${it.amount}\nRemarks: ${it.remarks}\n\nRegerds\nMasterCard-MPEMS"
								try{
									mailService.sendMail {
										to user.email
										from "mpems.mastercard@gmail.com"
										subject "MasterCard MPEMS Notification"
										body "Dear user, \n${content}\n\nRegerds\nMasterCard-MPEMS"
									 }
								}
								catch (e){
									log.info("email failed to send")
								}
							}
						}
					}					
				}			
			}
		}
		
	}
	
	
	def getCardTransactions(Card card, Date fromDate, Date toDate) {
		def qry = Transaction.where {
			card==card && txnDate >= fromDate && txnDate <= toDate
		}
		def transactions = qry.list(sort:"txnDate", order:"desc")
		return transactions
	}

	def roundNumber(double d) {
		return Math.round(d*100)/100.0d;
	}

	def getSpendingToday(User user) {
		def today = new Date()
		def cal = Calendar.getInstance()
		cal.add(Calendar.DATE, -1)
		Date fromDate = cal.getTime()
		def todayTxns = getUserTransactions(user, fromDate, today)
		def todaySpend = 0
		for (Transaction txn in todayTxns) {
			todaySpend += txn.amount
		}
		return roundNumber(todaySpend)
	}

	def getSpendingLastWeek(User user) {
		def today = new Date()
		def cal = Calendar.getInstance()
		cal.add(Calendar.DATE, -7)
		def lastWeek = cal.getTime()
		def lastWeekTxns = getUserTransactions(user, lastWeek, today)
		def lastWeekSpend = 0
		for (Transaction txn in lastWeekTxns) {
			lastWeekSpend += txn.amount
		}
		return roundNumber(lastWeekSpend)
	}

	def getSpendingLastMonth(User user) {
		def today = new Date()
		def cal = Calendar.getInstance()
		cal.add(Calendar.MONTH, -1)
		def lastMonth = cal.getTime()
		def lastMonthTxns = getUserTransactions(user, lastMonth, today)
		def lastMonthSpend = 0
		for (Transaction txn in lastMonthTxns) {
			lastMonthSpend += txn.amount
		}
		return roundNumber(lastMonthSpend)
	}

	def getSpendingLastYear(User user) {
		def today = new Date()
		def cal = Calendar.getInstance()
		cal.add(Calendar.YEAR, -1)
		def lastYear = cal.getTime()
		def lastYearTxns = getUserTransactions(user, lastYear, today)
		def lastYearSpend = 0
		for (Transaction txn in lastYearTxns) {
			lastYearSpend += txn.amount
		}
		return roundNumber(lastYearSpend)
	}

	def getSummary(User user) {
		def map = new HashMap()
		map.put("today_spend", getSpendingToday(user))
		map.put("week_spend", getSpendingLastWeek(user))
		map.put("month_spend", getSpendingLastMonth(user))
		map.put("year_spend", getSpendingLastYear(user))
		map.put("weekly_budget", getBudgetTarget(true, "WEEKLY", user))
		map.put("monthly_budget", getBudgetTarget(true, "MONTHLY", user))
		map.put("weekly_target", getBudgetTarget(false, "WEEKLY", user))
		map.put("monthly_target", getBudgetTarget(false, "MONTHLY", user))
		return map
	}
	
	def getBudgetTarget(isBudget, type, user){
		log.info 'bugdet target value'
		Budget budget = Budget.findByBudgetAndTypeAndUserAndBudgetType(isBudget, Budget.Type.ALL, user, Budget.BudgetType.valueOf(type))
		if(budget){
			return budget.amount
		}
		return '0'
	}
	def getTransactionsOfCards(List<Card>cards, Date fromDate, Date toDate) {
		def transactions = new ArrayList()
		for (card in cards) {
			def cardTxns = getCardTransactions(card, fromDate, toDate)
			transactions.addAll(cardTxns)
		}
		return transactions
	}
	
	def getUserTransactions(User user, Date fromDate, Date toDate) {
		def cards = cardService.getCards(user)
		return getTransactionsOfCards(cards, fromDate, toDate)
	}

	def getSpending(List<Transaction> transactions) {
		// Calculate
		def spending = 0
		for (txn in transactions) {
			spending += txn.amount
		}
		return roundNumber(spending)
	}

	def getMonth(Date date) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(date)
		return cal.get(Calendar.MONTH)
	}

	def getDayFirstMonth(Date date) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(date)
		cal.clear(Calendar.HOUR_OF_DAY);
		cal.clear(Calendar.MINUTE);
		cal.clear(Calendar.SECOND);
		cal.clear(Calendar.MILLISECOND);

		// get start of the month
		cal.set(Calendar.DAY_OF_MONTH, 1);
		def firstMonth = cal.getTime()
		cal.setTime(date)
		return firstMonth
	}

	def getDayLastMonth(Date date) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(date)
		cal.clear(Calendar.HOUR_OF_DAY);
		cal.clear(Calendar.MINUTE);
		cal.clear(Calendar.SECOND)
		cal.clear(Calendar.MILLISECOND)
		cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH))
		def lastMonth = cal.getTime()
		cal.setTime(date)
		return lastMonth
	}

	def getTotalSpendingOfCards(List<Card>cards, Date fromDate, Date toDate) {
		Calendar cal = Calendar.getInstance();
		SimpleDateFormat format = new SimpleDateFormat("MMM/yy")

		def spendings = new ArrayList()

		for (card in cards) {
			HashMap map = new HashMap()
			map.put("card", card)

			ArrayList cardSpending = new ArrayList()
			ArrayList timeSpending = new ArrayList()

			cal.setTime(fromDate)
			def start = fromDate
			Date lastMonth = getDayLastMonth(fromDate)
			while (true) {
				def end = false
				if (lastMonth.after(toDate)) {
					lastMonth = toDate
					end = true
				}
				def cardTxns = getCardTransactions(card, start, lastMonth)
				cardSpending.add(getSpending(cardTxns))
				timeSpending.add(format.format(cal.getTime()))

				if (end) {
					break
				}

				// Roll to next month
				cal.add(Calendar.MONTH, 1)
				start = getDayFirstMonth(cal.getTime())
				lastMonth = getDayLastMonth(cal.getTime())
			}

			map.put("spending", cardSpending)
			map.put("time", timeSpending)
			map.put("bank", card.bank)
			spendings.add(map)
		}

		return spendings
	}

	/**
	 * Retrieve total spending for last 12 months
	 * Total spending should be break into each individual card
	 * @param fromDate
	 * @param toDate
	 * @return
	 */
	def getTotalSpending(User user, Date fromDate, Date toDate) {
		def cards = cardService.getCards(user)
		return getTotalSpendingOfCards(cards, fromDate, toDate)

		// Loop through the month fromDate -> toDate

		// Add the total spending for the transaction if
		// it is in this month

		// Return array list
	}
	
	def getCategorySpendingForCards(ArrayList<Card> cards, Date fromDate, Date toDate) {
		def categorySpendings = new ArrayList()
		
		for (int i=0; i<cards.size(); i++) {
			def map = new HashMap()
			def card = cards.get(i)
			def categoryMap = new HashMap()
			def txns = getCardTransactions(card, fromDate, toDate)
			for (Transaction txn in txns) {
				def txnCategory = TransactionCategory.findByTransaction(txn)
				if (categoryMap.containsKey(txnCategory.category.name)) {
					
					def val = categoryMap.get(txnCategory.category.name)
					categoryMap.put(txnCategory.category.name, val + txn.amount)
				} else {
					categoryMap.put(txnCategory.category.name, txn.amount)
				}
				
			}
			map.put("card", card.brand + " - " + card.bank.name)
			map.put("spendings", categoryMap)
			categorySpendings.add(map)
		}
		return categorySpendings
	}

	def getCategorySpendingOfTxns(List<Transaction> txns) {
		HashMap categorySpendings = new HashMap()
		for (Transaction txn in txns) {
			def txnCategory = TransactionCategory.findByTransaction(txn)
			if (categorySpendings.containsKey(txnCategory.category)) {
				def val = categorySpendings.get(txnCategory.category)
				categorySpendings.put(txnCategory.category.name, val + txn.amount)
			} else {
				categorySpendings.put(txnCategory.category.name, txn.amount)
			}
		}
		return categorySpendings
	}
	
	/**
	 * Retrieve spending, 
	 * break into categories
	 * @param fromDate
	 * @param toDate
	 * @return
	 */
	def getCategorySpending(User user, Date fromDate, Date toDate) {
		def txns = getUserTransactions(user, fromDate, toDate)
		return getCategorySpendingOfTxns(txns)
	}

	/**
	 * Get spending detail for current month
	 * 1. top 3 credit card with the most spending
	 * 2. TOP 3 category with the most spending
	 */
	def getTopCardSpendingSummary(User user, Date fromDate, Date toDate) {
		def cards = cardService.getCards(user)
		def transactions = new ArrayList()

		for (card in cards) {
			def cardTxns = getCardTransactions(card, fromDate, toDate)
			def totalCardTxnAmount = 0
			for (Transaction txn in cardTxns) {
				totalCardTxnAmount += txn.amount
			}
			HashMap cardTxnMap = new HashMap()
			cardTxnMap.put("card", card)
			cardTxnMap.put("amount", roundNumber(totalCardTxnAmount))
			transactions.add(cardTxnMap)
		}

		// Sort the arraylist, amount descending
		Collections.sort(transactions, new Comparator<HashMap>() {
					public int compare(HashMap one, HashMap other) {
						return -one.get('amount').compareTo(other.get('amount'));
					}
				});
		for (int i=0; i<transactions.size(); i++) {
			if (i >=2) {
				transactions.remove(i)
			}
		}
		return transactions;
	}

	def getTopCategorySpendingSummary(User user, Date fromDate, Date toDate) {
		HashMap categorySpendings = getCategorySpending(user, fromDate, toDate)
		def topArray = new ArrayList()
		for (String categoryName in categorySpendings.keySet()) {
			HashMap map = new HashMap()
			map.put("category", categoryName)
			map.put("amount", roundNumber(categorySpendings.get(categoryName)))
			topArray.add(map)
		}
		Collections.sort(topArray, new Comparator<HashMap>() {
					public int compare(HashMap one, HashMap other) {
						return -one.get('amount').compareTo(other.get('amount'));
					}
				});

		for (int i=0; i<topArray.size(); i++) {
			if (i >=2) {
				topArray.remove(i)
			}
		}
		return topArray
	}
}
